package cn.sylinx.horm.spring.transaction;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import cn.sylinx.horm.core.datasource.DataSourceConnectionProvicer;
import cn.sylinx.horm.core.datasource.NamedDataSource;
import cn.sylinx.horm.core.datasource.TransactionalConnectionProvider;
import cn.sylinx.horm.exception.TransactionException;
import cn.sylinx.horm.util.GLog;

/**
 * 基于Spring，获取链接，支持事务托管Spring
 * 
 * @author johnhan
 *
 */
public class SpringDataSourceConnectionProvider extends DataSourceConnectionProvicer
		implements TransactionalConnectionProvider {
	
	protected Method getConnectionMethod;
	protected Method releaseConnectionMethod;

	private void initSpringInfosByRefect() {
		Class<?> dataSourceUtilClass;
		try {
			dataSourceUtilClass = Class.forName("org.springframework.jdbc.datasource.DataSourceUtils");
		} catch (ClassNotFoundException e) {
			throw new TransactionException(
					"Error:spring-jdbc jar missing, fail to build SpringDataSourceConnectionProvider.", e);
		}
		try {
			getConnectionMethod = dataSourceUtilClass.getMethod("getConnection", DataSource.class);
			releaseConnectionMethod = dataSourceUtilClass.getMethod("releaseConnection", Connection.class,
					DataSource.class);
		} catch (Exception e) {
			throw new TransactionException("Error: SpringDataSourceConnectionProvider initialize failed.", e);
		}
	}

	public SpringDataSourceConnectionProvider() {
	}

	public SpringDataSourceConnectionProvider(NamedDataSource namedDataSource) {
		super(namedDataSource);
		initSpringInfosByRefect();
	}

	@Override
	public Connection getConnection() throws SQLException {
		NamedDataSource namedDataSource = getNamedDataSource();
		if (namedDataSource == null) {
			throw new TransactionException("data source not set");
		}
		try {
			return (Connection) getConnectionMethod.invoke(null, namedDataSource.getDataSource());
		} catch (Exception e) {
			GLog.error("getConnection error in Spring", e);
			throw new TransactionException(e);
		}
	}

	@Override
	public void releaseConnection(Connection conn) throws SQLException {
		NamedDataSource namedDataSource = getNamedDataSource();
		if (namedDataSource == null) {
			throw new TransactionException("data source not set");
		}
		try {
			releaseConnectionMethod.invoke(null, conn, namedDataSource.getDataSource());
		} catch (Exception e) {
			GLog.error("releaseConnection error in Spring", e);
			throw new TransactionException(e);
		}
	}

	@Override
	public void startTransaction() {
		throw new TransactionException("not support in Spring environment, please use Spring's method directly");
	}

	@Override
	public void startTransaction(int isolationLevel) {
		throw new TransactionException("not support in Spring environment, please use Spring's method directly");
	}

	@Override
	public boolean isInTransaction() {
		throw new TransactionException("not support in Spring environment, please use Spring's method directly");
	}

	@Override
	public void commitTransaction() throws TransactionException {
		throw new TransactionException("not support in Spring environment, please use Spring's method directly");
	}

	@Override
	public void rollbackTransaction() {
		throw new TransactionException("not support in Spring environment, please use Spring's method directly");
	}
}